package org.zeapp.opencloud.framework.cloud;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

import org.zeapp.opencloud.framework.preference.Preference;

/**
 * The basic service for managing a set of Cloud Drivers. Applications can
 * declare their driver using ServiceLoader META-INF file or call explicitly
 * Class.forName() on their Cloud Drivers to allows them to register to this
 * manager.
 * 
 * @author Amphiprion
 * 
 */
public class CloudDriverManager {
	private static boolean initialized = false;

	/** The registered drivers **/
	private static List<CloudDriver> cloudDrivers = new ArrayList<CloudDriver>();

	/**
	 * Registers the given driver with the DriverManager. A newly-loaded driver
	 * class should call the method registerDriver to make itself known to the
	 * DriverManager.
	 * 
	 * @param driver
	 *            the driver to register
	 */
	public static synchronized void registerDriver(CloudDriver driver) {
		if (!initialized) {
			initialize();
		}
		cloudDrivers.add(driver);
	}

	/**
	 * Drops a driver from the DriverManager's list.
	 * 
	 * @param driver
	 *            the driver to drop
	 */
	public static synchronized void deregisterDriver(CloudDriver driver) {
		if (!initialized) {
			initialize();
		}
		cloudDrivers.remove(driver);
	}

	/**
	 * Return all registered drivers.
	 * 
	 * @return all registered drivers
	 */
	public static List<CloudDriver> getDrivers() {
		if (!initialized) {
			initialize();
		}
		return new ArrayList<CloudDriver>(cloudDrivers);
	}

	/**
	 * Attempts to locate a driver that understands the given identifier. The
	 * DriverManager attempts to select an appropriate driver from the set of
	 * registered cloud drivers.
	 * 
	 * @param indentifier
	 *            the identifier of the needed driver
	 * @return the cloud driver managing the given mode
	 * @throws CloudException
	 *             if a cloud exception occurs
	 */
	public static synchronized CloudDriver getDriver(String identifier)
			throws CloudException {
		if (!initialized) {
			initialize();
		}
		for (CloudDriver driver : cloudDrivers) {
			if (driver.accepts(identifier)) {
				return driver;
			}
		}

		throw new CloudException("No suitable driver", "ZECLD00001");

	}

	/**
	 * Attempts to retrieve the chunking service for the given identifier.
	 * 
	 * @param identifier
	 *            Attempts to retrieve the cloud service for the given
	 *            identifier. It is equivalent to
	 *            "getDriver(identifier).getChunker(preference)".
	 * @param preference
	 *            the preference to use.
	 * @return the cloud service managing the given mode
	 * @throws CloudException
	 *             if a cloud exception occurs
	 */
	public static synchronized Cloud getCloud(String identifier,
			Preference preference) throws CloudException {
		if (!initialized) {
			initialize();
		}
		return getDriver(identifier).getCloud(preference);
	}

	/**
	 * Use the ServiceLoader to register declared service.
	 */
	private static void initialize() {
		if (initialized) {
			return;
		}
		System.out.println("Load CloudDriver implementation via ServiceLoader");
		initialized = true;
		ServiceLoader<CloudDriver> ldr = ServiceLoader.load(CloudDriver.class);
		for (CloudDriver provider : ldr) {
			System.out.println(" found: " + provider.getClass());
		}
	}
}
